package grabcad

import (
	"encoding/json"
	"fmt"
	"github.com/gogf/gf/frame/g"
	"github.com/gogf/gf/os/gtime"
	"github.com/gogf/gf/util/gconv"
	"github.com/gomodule/redigo/redis"
	"github.com/tebeka/selenium"
	"github.com/xuri/excelize/v2"
	"grabcad_spider/utils"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"sort"
	"strings"
	"time"
)

type Article struct {
	Title       string   `json:"title"`
	Description string   `json:"description"`
	Categories  []string `json:"categories"`
	Url         string   `json:"url"`
	Dir         string   `json:"dir"`
}

func GetList(url string) ([]string, error) {
	fmt.Println("grabcad列表[开始处理]: " + " url :" + url)
	port := utils.RedisGetValidPort()
	_, webDriver, err := utils.GetWebDriver(utils.RedisGetValidPort())
	defer utils.RedisReleasePort(port)
	defer webDriver.Quit()
	if err != nil {
		fmt.Println("grabcad列表[处理出错]: ", err)
		return nil, err
	}
	webDriver.Get(url)
	utils.RandomSleep()

	var grabList []string

	cadListItems, err := webDriver.FindElements(selenium.ByXPATH, "//div[@class='infoSection']")
	for _, item := range cadListItems {
		var GrabArticle Article

		hrefItem, err := item.FindElement(selenium.ByXPATH, ".//a")
		if err != nil || hrefItem == nil {
			fmt.Println("grabcad列表[处理出错]: ", err)
		} else {
			href, _ := hrefItem.GetAttribute("href")
			fmt.Println("href===", href)
			GrabArticle.Url = href
			grabList = append(grabList, href)
			//titleItem, _ := item.FindElement(selenium.ByXPATH, ".//span")
			//GrabArticle.Title, _ = titleItem.Text()
			GrabArticle.Title = utils.GetTitleFromUrl(href)

			text, _ := json.Marshal(GrabArticle)

			utils.SetGrabCADArticleToRedis(GrabArticle.Url, gconv.String(text))
		}

	}
	fmt.Println("grabcad列表[处理完成]: " + " url :" + url)

	return grabList, nil

}

// 登录
func Login() (*[]selenium.Cookie, error) {
	port := utils.RedisGetValidPort()
	_, webDriver, err := utils.GetWebDriver(utils.RedisGetValidPort())
	defer utils.RedisReleasePort(port)
	defer webDriver.Quit()
	if err != nil {
		fmt.Println("GetWebDriver err : ", err)

	}

	loginUrl := "https://grabcad.com/login"

	err = webDriver.Get(loginUrl)
	if err != nil {
		fmt.Println("webDriver.Get err : ", err)
	}
	utils.RandomSleep()

	account := utils.RandAccount()

	loginItem, err := webDriver.FindElement(selenium.ByXPATH, "//form[@id='login_form']")
	if err != nil {
		fmt.Println("loginButton err : ", err)
		log.Fatal("反爬虫机制触发,wait 10 minutes")
	}
	utils.RandomSleep()
	inputEmail, err := loginItem.FindElement(selenium.ByXPATH, "//input[@name='member[email]']")
	if err != nil {
		fmt.Println("inputEmail err : ", err)
		log.Fatal("反爬虫机制触发,wait 10 minutes")
	}
	err = inputEmail.SendKeys(account.Email)
	if err != nil {
		fmt.Println("inputEmail.SendKeys err : ", err)
	}
	utils.RandomSleep()
	inputPassword, _ := webDriver.FindElement(selenium.ByXPATH, "//input[@name='member[password]']")
	inputPassword.SendKeys(account.Password)
	utils.RandomSleep()

	submitButton, _ := webDriver.FindElement(selenium.ByXPATH, "//button[@type='submit']")
	err = submitButton.Click()
	if err != nil {
		fmt.Println(err)
		return nil, err
	}
	utils.RandomSleep()

	//获取cookie
	cookies, err := webDriver.GetCookies()
	if err != nil {
		fmt.Println("GetCookies err : ", err)
	}
	fmt.Println("cookies:", cookies)

	return &cookies, nil
}

// 详情页

func GetDetail(url string) error {

	redis, err := utils.GetGrabCADArticleFromRedis(url)
	if err != nil || redis == "" {
		fmt.Println("grabcad详情[处理出错]: ", err)
		return err
	}

	article := Article{}
	json.Unmarshal([]byte(redis), &article)

	status, _ := utils.RedisGetItemStatus(url)
	if status == utils.URL_STATUS_CRAWLED {
		fmt.Println("grabcad详情[跳过详情]:" + " url :" + url + " 已经处理或正在处理中")
		return nil
	}
	utils.RedisSetItemStatus(url, utils.URL_STATUS_PROCESSING)

	cookies, err := Login()

	if err != nil || cookies == nil {
		fmt.Println("grabcad详情[处理出错]: ", err)
		utils.RedisSetItemStatus(url, utils.URL_STATUS_FAIL)
		return err
	}

	article.Dir = utils.WeekTime(article.Title)

	port := utils.RedisGetValidPort()
	_, webDriver, err := utils.GetWebDriverDownload(utils.RedisGetValidPort(), article.Dir)
	defer utils.RedisReleasePort(port)
	defer webDriver.Quit()

	webDriver.Get(url)

	for _, cookie := range *cookies {
		webDriver.AddCookie(&cookie)
	}

	webDriver.Get(url)

	utils.RandomSleep()

	////获取标题
	//titleItem, err := webDriver.FindElement(selenium.ByXPATH, "//h1[@class='content-title title--fluid is-3 ng-binding']")
	//
	//if err != nil {
	//	fmt.Println("titleItem err : ", err)
	//}
	//
	//title, err := titleItem.Text()
	//if err != nil {
	//	fmt.Println("titleItem.Text err : ", err)
	//}
	//
	//fmt.Println("title:", title)

	//获取描述
	descItem, err := webDriver.FindElement(selenium.ByXPATH, "//div[@class='description']//p")

	if err != nil {
		fmt.Println("descItem err : ", err)
	}

	article.Description, err = descItem.Text()
	if err != nil {
		fmt.Println("descItem.Text err : ", err)
	}

	//获取分类
	categoriesItems, err := webDriver.FindElements(selenium.ByXPATH, "//div[@ui-view='sidebar']")

	categoriesItems, err = webDriver.FindElements(selenium.ByXPATH, ".//span[@ng-repeat='category in model.categories']//a")

	for _, categoryItem := range categoriesItems {
		text, _ := categoryItem.Text()
		fmt.Println(text)
		article.Categories = append(article.Categories, text)
	}

	//获取图片
	//picItems, err := webDriver.FindElements(selenium.ByXPATH, "//div[@class='carousel ng-scope']//div[contains(@class,'ng-scope slick-slide')]//img[@class='image-content ng-scope']")
	picItems, err := webDriver.FindElements(selenium.ByXPATH, "//div[@class='carousel ng-scope']//div[contains(@class,'ng-scope slick-slide')]")
	for _, picItem := range picItems {
		button3D, err := picItem.FindElement(selenium.ByXPATH, ".//div[@class='actions ng-hide']")
		if button3D != nil && err == nil {

			img, _ := picItem.FindElement(selenium.ByXPATH, ".//img[contains(@class,'image-content ng-scope')]")
			src, _ := img.GetAttribute("src")
			if src == "" {
				src, _ = img.GetAttribute("data-lazy")
			}

			fmt.Println("src:", src)
			utils.DownloadImageGrab(src, article.Dir)
		}

	}
	//fileItems, err := webDriver.FindElements(selenium.ByXPATH, "//div[@class='fileTable ng-scope']//div[contains(@class,'row ng-scope')]")
	//for _, fileItem := range fileItems {
	//
	//	typeItem, _ := fileItem.FindElement(selenium.ByXPATH, ".//div[@class='filetype ng-binding']")
	//
	//	typeText, _ := typeItem.Text()
	//	if typeText != "png" && typeText != "jpg" && typeText != "jpeg" {
	//		img, _ := fileItem.FindElement(selenium.ByXPATH, ".//img")
	//		src, _ := img.GetAttribute("src")
	//		src = strings.Replace(src, "tiny.png", "large.png", -1)
	//		fmt.Println("src:", src)
	//		utils.DownloadImageGrab(src, article.Dir)
	//	}
	//}

	//点击下载
	downloadButtonItem, err := webDriver.FindElement(selenium.ByXPATH, "//button[@class='download btn btnLg btnRed']")

	_, err = webDriver.ExecuteScript("arguments[0].click();", []interface{}{downloadButtonItem})

	maxRetries := 3
	retryCount := 0
	timeout := 30 * time.Minute // 设置超时时间为 10 秒

	for retryCount < maxRetries {

		err = webDriver.WaitWithTimeout(func(wd selenium.WebDriver) (bool, error) {
			utils.RandomSleep()

			for i := 0; i < 300; i++ { // 等待 300 秒
				files, err := ioutil.ReadDir(article.Dir)
				if err != nil {
					fmt.Println(err)
				}
				// 对文件列表按照修改时间进行排序 然后获取最新文件
				sort.Slice(files, func(i, j int) bool {
					return files[i].ModTime().After(files[j].ModTime())
				})
				utils.RandomSleep()
				if len(files) > 0 {
					lastFile := files[0]
					if !utils.IsFileDownloading(lastFile.Name()) {
						fmt.Println("grabcad资源[下载完成]:" + " url :" + url)
						return true, nil
					}
				}
				utils.RandomSleep()
			}
			return false, nil
		}, timeout)

		if err == nil {
			break
		}

		fmt.Println("grabcad资源[网络失败]:", err)
		utils.RedisSetItemStatus(url, utils.URL_STATUS_FAIL)
		retryCount++
		_, err = webDriver.ExecuteScript("arguments[0].click();", []interface{}{downloadButtonItem})

		// 删除 crdownload 文件
		files, err := ioutil.ReadDir(article.Dir)
		if err != nil {
			fmt.Println(err)
		}
		for _, file := range files {
			if strings.HasSuffix(file.Name(), ".crdownload") {
				err = os.Remove(filepath.Join(article.Dir, file.Name()))
				if err != nil {
					fmt.Println(err)
				}
			}
		}

		time.Sleep(5 * time.Second) // 等待 5 秒钟
	}

	if retryCount >= maxRetries {
		fmt.Println("grabcad资源[达到最大重试次数]:", err)
	}

	utils.RandomSleep()

	text, _ := json.Marshal(article)
	utils.SetGrabCADResultToRedis(url, gconv.String(text))

	fmt.Println("grabcad详情[处理完成]: " + " url :" + url)
	utils.RedisSetItemStatus(url, utils.URL_STATUS_CRAWLED)

	return nil
}

func ExportExcel() {
	conn := g.Redis().Conn()
	defer conn.Close()
	values, err := redis.StringMap(conn.Do("HGETALL", utils.CGABCAD_RESULT_REDIS))
	if err != nil {
		fmt.Println("grabcad[导出出错]", err)
	}
	arData := make([]*Article, 0)

	for _, value := range values {
		ninjaInfo := &Article{}
		err = json.Unmarshal(gconv.Bytes(value), ninjaInfo)
		if err != nil {
			fmt.Println("grabcad[导出出错]", err)
		}
		arData = append(arData, ninjaInfo)

	}
	f := excelize.NewFile()
	index, err := f.NewSheet("grabcad")
	if err != nil {
		fmt.Println("grabcad[导出出错]:" + err.Error())
		return
	}
	defer f.Close()
	f.SetCellValue("grabcad", "A1", "标题")
	f.SetCellValue("grabcad", "B1", "简介")
	f.SetCellValue("grabcad", "C1", "分类")

	for index, element := range arData {
		lineNumber := index + 2
		titleCell := fmt.Sprintf("A%d", lineNumber)
		descCell := fmt.Sprintf("B%d", lineNumber)
		categoryCell := fmt.Sprintf("C%d", lineNumber)

		categories := strings.Join(element.Categories, ",")
		f.SetCellValue("grabcad", titleCell, element.Title)
		f.SetCellValue("grabcad", descCell, element.Description)
		f.SetCellValue("grabcad", categoryCell, categories)
	}

	f.SetActiveSheet(index)
	fileName := fmt.Sprintf("grabcad_%s.xlsx", gtime.Now().Format("Ymd_His"))
	err = f.SaveAs(fileName)
	if err != nil {
		fmt.Println("grabcad[导出出错]:" + err.Error())
		return
	}

	fmt.Println("grabcad[导出成功]:" + fileName)
}
